package com.lds.config.center.client.autoconfigure;

import com.lds.config.center.client.common.constant.ClientConstant;
import com.lds.config.center.client.common.model.response.GetAllItemResponse;
import com.lds.config.center.client.service.ClientService;
import com.lds.config.center.client.service.ClientServiceImpl;
import com.lds.management.center.common.model.dto.ResultDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.Ordered;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.MutablePropertySources;

import java.util.HashMap;
import java.util.Map;

@Slf4j
public class ConfigCenterClientApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {

    @Override
    public void initialize(@NotNull ConfigurableApplicationContext applicationContext) {
        Map<String, Map<String, Object>> config = config(applicationContext);
        loadConfig(applicationContext, config);
    }

    private void fillProperties(ConfigurableApplicationContext applicationContext) {
        ConfigurableEnvironment environment = applicationContext.getEnvironment();
        setProperty(ClientConstant.CACHE_FILE_ROOT_PATH, environment);
        setProperty(ClientConstant.URL_PROPERTY_NAME, environment);
        setProperty(ClientConstant.ENV_NAME_PROPERTY_NAME, environment);
        setProperty(ClientConstant.CLUSTER_NAME_PROPERTY_NAME, environment);
        setProperty(ClientConstant.APP_NAME_PROPERTY_NAME, environment);
        setProperty(ClientConstant.NAMESPACE_NAMES_PROPERTY_NAME, environment);
    }

    private void setProperty(String key, ConfigurableEnvironment environment) {
        String value = environment.getProperty(key);
        if (StringUtils.isNotEmpty(value)) {
            System.setProperty(key, value);
        }
    }

    private Map<String, Map<String, Object>> config(ConfigurableApplicationContext applicationContext) {
        fillProperties(applicationContext);
        ClientService clientService = ClientServiceImpl.getInstance();

        ResultDTO<GetAllItemResponse> resultDTO = clientService.getAllItem();
        if (ResultDTO.isSuccess(resultDTO)) {
            // 加载远程配置
            return remoteConfig(resultDTO.getData().getNamespaceMap());
        } else {
            // 从本地文件中加载
            Map<String, Map<String, Object>> localProperties = clientService.getAllItemFromLocal();
            log.info("从本地文件中加载配置项: {}", localProperties);
            return localProperties;
        }
    }

    private Map<String, Map<String, Object>> remoteConfig(Map<String, GetAllItemResponse.Namespace> namespaceMap) {
        Map<String, Map<String, Object>> remoteConfig = new HashMap<>();
        namespaceMap.forEach((namespaceName, namespace) -> {
            Map<String, Object> namespaceConfig = new HashMap<>();
            for (GetAllItemResponse.Item item : namespace.getItems()) {
                namespaceConfig.put(item.getName(), item.getValue());
            }
            remoteConfig.put(namespaceName, namespaceConfig);
        });
        return remoteConfig;
    }

    private void loadConfig(ConfigurableApplicationContext applicationContext, Map<String, Map<String, Object>> config) {
        MutablePropertySources propertySources = applicationContext.getEnvironment().getPropertySources();
        CompositePropertySource composite = new CompositePropertySource("config-center-client");

        config.forEach((namespaceName, namespaceConfig) -> {
            MapPropertySource mapPropertySource = new MapPropertySource(namespaceName, namespaceConfig);
            composite.addPropertySource(mapPropertySource);
        });

        propertySources.addFirst(composite);
    }

    @Override
    public int getOrder() {
        return Integer.MAX_VALUE;
    }
}
